/*
    Copyright 2006 Todi.
    Send feedback and suggestions to todi@peerfeeds.com.  
  
    This file is part of Peerfeeds.

    Peerfeeds is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    Peerfeeds is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Peerfeeds; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA 
  
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Collections;
using System.Data;
using System.Text;
using System.IO;
using System.Xml;
using System.Configuration;
using System.Text.RegularExpressions;

namespace Peerfeeds
{
    public class Xml
    {
        private XmlTextReader xmlReader;
        private XmlDocument xmlDoc;

        public string header;
        public string item;
        public string footer;

        // Constructor
        public Xml(string file)
        {
            FileStream read = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);

            // Tried to read xml and ignoring illegal characters, like YnHub does. Unfortunately it seems hard.
            // The whole point of XML is that it's _always_ supposed to be well formed. YnHub does it buggy ;)

            xmlReader = new XmlTextReader(read);
            xmlDoc = new XmlDocument();

            // Load the XML content into a XmlDocument
            xmlDoc.Load(xmlReader);
            xmlReader.Close();
            read.Close();
        }

        public Xml()
        {
        }

        public void Template(string path)
        {
            XmlTextReader tpReader = new XmlTextReader(path);
            XmlDocument tp = new XmlDocument();

            //try
            //{
                // Load the XML content into a XmlDocument
                tp.Load(tpReader);

                for (int i = 0; i < tp.ChildNodes[0].ChildNodes.Count; i++)
                {
                    if (tp.ChildNodes[0].ChildNodes[i].Name == "Header")
                    {
                        header = tp.ChildNodes[0].ChildNodes[i].InnerText.ToString();
                    }
                    else if (tp.ChildNodes[0].ChildNodes[i].Name == "Item")
                    {
                        item = tp.ChildNodes[0].ChildNodes[i].InnerText.ToString();
                    }
                    else if (tp.ChildNodes[0].ChildNodes[i].Name == "Footer")
                    {
                        footer = tp.ChildNodes[0].ChildNodes[i].InnerText.ToString();
                    }
                }
            //}
            //catch
            //{
            //    // Fugly default header and item templates in case we don't have default.template

            //    header = "DEFAULT---------------------" + Environment.NewLine +
            //             "RSS feed from %title%" + Environment.NewLine +
            //             "Visit at %link%" + Environment.NewLine +
            //             "---------------------" + Environment.NewLine;

            //    item = "%title%" + Environment.NewLine +
            //           "%link%" + Environment.NewLine +
            //           "%description%" + Environment.NewLine +
            //           "------" + Environment.NewLine;

            //    footer = "";
            //}
            //finally
            //{
                tpReader.Close();
            //}
        }

        private void WriteTemplate()
        {

        }

        public void WriteXmlDoc(string file)
	    {
            // Save XML document 
            FileStream writer = new FileStream(file, FileMode.Truncate, FileAccess.Write, FileShare.Read);
            
            xmlDoc.Save(writer);
            writer.Close();
	    }

        public class Feed
        {
            public string url = "";
            public string textCommand = "";
            public int items = Convert.ToInt32(ConfigurationManager.AppSettings["MaxItems"]);
            public string template = "";
            public bool stripHTML = Convert.ToBoolean(ConfigurationManager.AppSettings["StripHTML"]);
            public bool reverse = Convert.ToBoolean(ConfigurationManager.AppSettings["Reverse"]);
            public bool active = false;
            public DateTime lastModified;
            public Hashtable header;
        }

        public void WriteTextCommand(string command, string content)
        {
            if (!UpdateTextCommand(command, content))
            {
                InsertTextCommand(command, content);
            }
        }
        
        // TODO: This looks pretty ugly, it should be cleaned up if possible
        public bool UpdateTextCommand(string command, string content)
        {
            // Need to do a bit of replacing
            content = EscapeChars(content);

            bool ok = false;

            // Loop for the <TextCommands> tag
            for (int i = 0; i < xmlDoc.ChildNodes.Count; i++)
            {
                // If it is the TextCommands tag
                if (xmlDoc.ChildNodes[i].Name == "TextCommands")
                {
                    // Loop for the <TextCommand> tag
                    for (int x = 0; x < xmlDoc.ChildNodes[i].ChildNodes.Count; x++)
                    {
                        // If it is the TextCommand tag
                        if (xmlDoc.ChildNodes[i].ChildNodes[x].Name == "TextCommand")
                        {
                            for (int y = 0; y < xmlDoc.ChildNodes[i].ChildNodes[x].ChildNodes.Count; y++)
                            {
                                // If it is the item tag, then it has children tags which we will add
                                XmlNode node = xmlDoc.ChildNodes[i].ChildNodes[x].ChildNodes[y];

                                if (node.Name == "Name" && node.InnerText == command)
                                {
                                    XmlNode newNode = xmlDoc.CreateElement("Content"); 
                                    newNode.InnerText = content;

                                    xmlDoc.ChildNodes[i].ChildNodes[x].ReplaceChild(newNode, xmlDoc.ChildNodes[i].ChildNodes[x]["Content"]);

                                    ok = true;
                                }
                            }
                        }
                    }
                }
            }

            return ok;
        }

        public void WriteTextFile(string filename, string content)
        {
            // Need to do a bit of replacing
            content = EscapeChars(content);

            StreamWriter strWriter = new StreamWriter(filename + ".txt", false);
            strWriter.Write(content);

            strWriter.Flush();
            strWriter.Close();
        }

        public void InsertTextCommand(string command, string content)
        {
            // Need to do a bit of replacing
            content = EscapeChars(content);

            StringBuilder xmlstr = new StringBuilder();

            // TODO: Bit of cheating here, should do this the proper xml addnode etc way.. probably
            xmlstr.Append(Environment.NewLine);
            xmlstr.Append("    <Name>" + command + "</Name>");
            xmlstr.Append(Environment.NewLine);
            xmlstr.Append("    <OverrideMuted enable=\"false\"/>");
            xmlstr.Append(Environment.NewLine);
            xmlstr.Append("    <Enabled enable=\"true\"/>");
            xmlstr.Append(Environment.NewLine);
            xmlstr.Append("    <EditLevel>5</EditLevel>");
            xmlstr.Append(Environment.NewLine);
            xmlstr.Append("    <AccessMinLevel>0</AccessMinLevel>");
            xmlstr.Append(Environment.NewLine);
            xmlstr.Append("    <AccessMaxLevel>9</AccessMaxLevel>");
            xmlstr.Append(Environment.NewLine);
            xmlstr.Append("    <ShowMinLevel>0</ShowMinLevel>");
            xmlstr.Append(Environment.NewLine);
            xmlstr.Append("    <ShowMaxLevel>9</ShowMaxLevel>");
            xmlstr.Append(Environment.NewLine);
            xmlstr.Append("    <MainchatCommandVisibilit>1</MainchatCommandVisibilit>");
            xmlstr.Append(Environment.NewLine);
            xmlstr.Append("    <Schedule>0</Schedule>");
            xmlstr.Append(Environment.NewLine);
            xmlstr.Append("    <Autorun>0</Autorun>");
            xmlstr.Append(Environment.NewLine);
            xmlstr.Append("    <BroadCast>0</BroadCast>");
            xmlstr.Append(Environment.NewLine);
            xmlstr.Append("    <MessageType>0</MessageType>");
            xmlstr.Append(Environment.NewLine);
            xmlstr.Append("    <Paramerters>0</Paramerters>");
            xmlstr.Append(Environment.NewLine);
            xmlstr.Append("    <Content>" + content + "</Content>");
            xmlstr.Append(Environment.NewLine);

            // Loop for the <TextCommands> tag
            for (int i = 0; i < xmlDoc.ChildNodes.Count; i++)
            {
                // If it is the TextCommands tag
                if (xmlDoc.ChildNodes[i].Name == "TextCommands")
                {
                    XmlNode newNode = xmlDoc.CreateElement("TextCommand");
                    newNode.InnerXml = xmlstr.ToString();

                    // Insert our new node
                    xmlDoc.ChildNodes[i].AppendChild(newNode);
                }
            }
        }

        private string EscapeChars(string content)
        {
            // Illegal DC characters must be replaced
            content = content.Replace("$", "&#36;");
            content = content.Replace("|", "&#124;");
            
            // Do a regular expressions check to change & into &amp; except when it's used in html
            content = Regex.Replace(content, "&(?![a-zA-Z]{2,6};|#[0-9]{3};)", "&amp;", RegexOptions.IgnoreCase);

            return content;
        }

        /*
          These are the ones i've found that YnHub encodes

          '&'   ->  '&amp;'
          #10   ->  '&#10;'
          #13   ->  '&#13;'
          '"'   ->  '&quot;'
          ''''  ->  '&apos;'
          '<'   ->  '&lt;'
          '>'   ->  '&gt;'
    
         */

        public ArrayList ReadFeedsFromXML(string file)
        {
            DateTime time = new DateTime();
            return ReadFeedsFromXML(file, time);
        }

        public ArrayList ReadFeedsFromXML(string file, DateTime lastCache)
        {
            // Check if the file has changed since last check
            if (File.GetLastWriteTime(file).CompareTo(lastCache) < 0)
            {
                return new ArrayList();
            }
            
            ArrayList list = new ArrayList();
            FileStream read = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);

            // This is where we store text_commands.xml and make all changes before writing it back            
            XmlDocument feedsDoc = new XmlDocument();

            // URL's to a feed can contain &'s which xml doesn't like, must be escaped before loaded
            StreamReader stream = new StreamReader(read);
            string data = stream.ReadToEnd().Replace("&", "&amp;");

            xmlReader = new XmlTextReader(new StringReader(data));

            // Load the XML content into a XmlDocument
            feedsDoc.Load(xmlReader);
            xmlReader.Close();
            read.Close();

            Feed feed;

            // Loop for <Peerfeeds> tag
            for (int i = 0; i < feedsDoc.ChildNodes.Count; i++)
            {
                // If it is the Peerfeeds tag
                if (feedsDoc.ChildNodes[i].Name == "Peerfeeds")
                {
                    // Loop for the <Feed> tag
                    for (int x = 0; x < feedsDoc.ChildNodes[i].ChildNodes.Count; x++)
                    {
                        // If it is the Feed tag
                        if (feedsDoc.ChildNodes[i].ChildNodes[x].Name == "Feed")
                        {
                            XmlNode node = feedsDoc.ChildNodes[i].ChildNodes[x];
                            feed = new Feed();

                            for (int xx = 0; xx < node.ChildNodes.Count; xx++)
                            {
                                string inner = node.ChildNodes[xx].InnerText;

                                if (inner != null && inner != "")
                                {
                                    switch (node.ChildNodes[xx].Name)
                                    {
                                        case "URL":
                                            feed.url = inner;
                                            break;
                                        case "TextCommand":
                                            feed.textCommand = inner;
                                            break;
                                        case "MaxItems":
                                            feed.items = Convert.ToInt32(inner);
                                            break;
                                        case "Template":
                                            feed.template = inner;
                                            break;
                                        case "StripHTML":
                                            feed.stripHTML = Convert.ToBoolean(inner);
                                            break;
                                        case "Reverse":
                                            feed.reverse = Convert.ToBoolean(inner);
                                            break;
                                        case "Active":
                                            feed.active = Convert.ToBoolean(inner);
                                            break;
                                        default:
                                            break;
                                    }
                                }
                                //feed.url = node["URL"].InnerText;
                                //feed.textCommand = node["TextCommand"].InnerText;
                                //feed.items = (node["MaxItems"].InnerText != "") ? Convert.ToInt32(node["MaxItems"].InnerText) : Convert.ToInt32(ConfigurationManager.AppSettings["MaxItems"]);
                                //feed.template = node["Template"].InnerText;
                                //feed.stripHTML = (node["StripHTML"].InnerText == "True" || node["StripHTML"].InnerText == "False") ? Convert.ToBoolean(node["StripHTML"].InnerText) : Convert.ToBoolean(ConfigurationManager.AppSettings["StripHTML"]);
                                //feed.reverse = (node["Reverse"].InnerText == "True" || node["Reverse"].InnerText == "False") ? Convert.ToBoolean(node["Reverse"].InnerText) : Convert.ToBoolean(ConfigurationManager.AppSettings["Reverse"]);

                            }
                            list.Add(feed);
                        }
                    }
                }
            }

            return list;
        }

    }
}
